package com.xianggu.spring.aop.config;

import com.xianggu.spring.aop.enums.AdviceType;
import com.xianggu.spring.aop.joinPoint.InterFaceJoinPoint;
import com.xianggu.spring.aop.joinPoint.JDKJoinPoint;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author chengjy
 * @date 2025/3/10
 */
public class AopJdkProxyFactory {

    public static Object getProxy(Object target , Advice advice){
        return Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        InterFaceJoinPoint joinPoint = new InterFaceJoinPoint(method, args, target);
                        try{
                            // 跳过toString方法
                            if("toString".equals(method.getName())){
                                return method.invoke(target, args);
                            }
                            // 前置增强
                            advice.before(joinPoint);

                            // 执行原始方法
                            Object result = method.invoke(target, args);

                            // 后置增强
                            advice.after(joinPoint);
                            return result;
                        }catch (RuntimeException e){
                            advice.afterThrowing(method, e);
                            throw e;
                        }finally {
                            // 最终增强
                            advice.afterReturning(method, null);
                        }
                    }
                });
    }

    public static Object getProxy(Object target, List<AdviceParam> adviceParams, List<String> noProxyMethods) {
        Map<AdviceType, List<AdviceParam>> typeListMap = adviceParams.stream().collect(Collectors.groupingBy(AdviceParam::getAdviceType));

        return Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        // 跳过toString方法 和 无需代理方法
                        if("toString".equals(method.getName()) || (noProxyMethods != null && noProxyMethods.contains(method.getName()) )){
                            return method.invoke(target, args);
                        }
                        JDKJoinPoint joinPoint = new JDKJoinPoint(method, args, target);
                        try{

                            // 前置增强
                            if (typeListMap.containsKey(AdviceType.BEFORE)) {
                                for (AdviceParam adviceParam : typeListMap.get(AdviceType.BEFORE)) {
                                    adviceParam.getMethod().invoke(adviceParam.getObject(), joinPoint);
                                }
                            }

                            Object result = null;
                            // 环绕增强
                            if (typeListMap.containsKey(AdviceType.AROUND)) {
                                for (AdviceParam adviceParam : typeListMap.get(AdviceType.AROUND)) {
                                    result = adviceParam.getMethod().invoke(adviceParam.getObject(), joinPoint);
                                }
                            }else {
                                // 执行原始方法
                                result = joinPoint.proceed();
                            }

                            // 后置增强
                            if (typeListMap.containsKey(AdviceType.AFTER)) {
                                for (AdviceParam adviceParam : typeListMap.get(AdviceType.AFTER)) {
                                    adviceParam.getMethod().invoke(adviceParam.getObject(), joinPoint);
                                }
                            }
                            return result;
                        }catch (RuntimeException e){
                            if (typeListMap.containsKey(AdviceType.AFTER_THROWING)) {
                                for (AdviceParam adviceParam : typeListMap.get(AdviceType.AFTER_THROWING)) {
                                    adviceParam.getMethod().invoke(adviceParam.getObject(), method, e);
                                }
                            }
                            throw e;
                        }finally {
                            // 最终增强
                            if (typeListMap.containsKey(AdviceType.AFTER_RETURNING)) {
                                for (AdviceParam adviceParam : typeListMap.get(AdviceType.AFTER_RETURNING)) {
                                    adviceParam.getMethod().invoke(adviceParam.getObject(), joinPoint);
                                }
                            }
                        }
                    }
                });
    }
}
